package com.onlyxiahui.extend.spring.webflux.dispatcher.result;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

import javax.annotation.PostConstruct;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.SynthesizingMethodParameter;
import org.springframework.http.server.PathContainer;
import org.springframework.util.StringUtils;
import org.springframework.web.reactive.HandlerResult;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.util.pattern.PathPattern;
import org.springframework.web.util.pattern.PathPatternParser;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.onlyxiahui.extend.action.spring.boot.properties.OnlyActionDispatcherProperties;
import com.onlyxiahui.extend.spring.webflux.dispatcher.result.handler.ResponseBodyHandler;
import com.onlyxiahui.framework.action.dispatcher.general.util.ActionDispatcherJsonUtil;

/**
 * 
 * 
 * <br>
 * Date 2019-12-03 14:44:10<br>
 * 
 * @author XiaHui [onlovexiahui@qq.com]<br>
 * @since 1.0.0
 */
@Aspect
@Configuration
@ConditionalOnProperty(prefix = "only.action.dispatcher", name = "spring-response-handle-enable", havingValue = "true", matchIfMissing = false)
@ConditionalOnClass(name = { "org.springframework.web.reactive.result.method.annotation.ResponseBodyResultHandler" })
public class OnlyResponseBodyHandleConfig {

	private static final PathPatternParser PATTERN_PARSER = new PathPatternParser();
	private List<PathPattern> excludeList = new ArrayList<>();

	@Autowired()
	private OnlyActionDispatcherProperties onlyActionDispatcherProperties;

	@Autowired(required = false)
	private List<ResponseBodyHandler> handlers;

	private SynthesizingMethodParameter methodParameter = null;
	private OnlyResponseBodyHandleController mc = new OnlyResponseBodyHandleController();

	@PostConstruct
	public void config() {

		String name = "message";
		Method[] ms = OnlyResponseBodyHandleController.class.getMethods();
		for (Method m : ms) {
			if (name.equals(m.getName())) {
				methodParameter = SynthesizingMethodParameter.forExecutable(m, 0);
			}
		}

		Set<String> list = onlyActionDispatcherProperties.getResponseHandleExcludePaths();
		if (null != list) {
			for (String pathExclude : list) {
				// exclude=[{"path":"/actuator"},{"path":"/actuator/*"}]
				if (!StringUtils.isEmpty(pathExclude) && ActionDispatcherJsonUtil.maybeJsonArray(pathExclude)) {
					JSONArray ja = JSONArray.parseArray(pathExclude);

					if (null != ja && !ja.isEmpty()) {
						int size = ja.size();
						for (int i = 0; i < size; i++) {
							JSONObject o = ja.getJSONObject(i);
							String path = (o).getString("path");
							if (!StringUtils.isEmpty(path)) {
								excludeList.add(PATTERN_PARSER.parse(path));
							}
						}
					}
				}
			}
		}
	}

	@Around(value = "execution(* org.springframework.web.reactive.result.method.annotation.ResponseBodyResultHandler.handleResult(..)) && args(exchange, result)", argNames = "point,exchange,result")
	public Object handleResult(ProceedingJoinPoint point, ServerWebExchange exchange, HandlerResult result) {

		// Object o = point.getTarget();

		Object value = null;
		// ResponseBodyResultHandler rb = (ResponseBodyResultHandler) o;
		PathContainer lookupPath = exchange.getRequest().getPath().pathWithinApplication();
		if (ignore(lookupPath, exchange)) {
			Object body = result.getReturnValue();
			value = body;
//			MethodParameter bodyTypeParameter = result.getReturnTypeSource();
//			value= rb.writeBody(body, bodyTypeParameter, exchange);
		} else {
			Object data = result.getReturnValue();
//			ArgumentBox argumentBox = exchange.getAttribute(ActionConstant.ARGUMENT_BOX);
//			String body = exchange.getAttribute(ActionConstant.REQUEST_BODY_CACHE);
			if (null != handlers) {
				for (ResponseBodyHandler h : handlers) {
					data = h.handle(exchange, result);
				}
			}
			value = data;
//			MethodParameter bodyTypeParameter = result.getReturnTypeSource();
//			value= rb.writeBody(data, bodyTypeParameter, exchange);
		}
		try {
//			return point.proceed(Arrays.asList(
//					exchange,
//					new HandlerResult(result.getHandler(), value, result.getReturnTypeSource())).toArray());
//			
			return point.proceed(Arrays.asList(
					exchange,
					new HandlerResult(mc, value, methodParameter)).toArray());
		} catch (Throwable e) {
			e.printStackTrace();
		}
		return value;
	}

//	@Bean
//	public ResponseBodyResultHandler bodyResultHandler(
//			ServerCodecConfigurer serverCodecConfigurer,
//			RequestedContentTypeResolver webFluxContentTypeResolver,
//			ReactiveAdapterRegistry webFluxAdapterRegistry) {
//		// @Order(-1)
//		return new ResponseBodyResultHandler(
//				serverCodecConfigurer.getWriters(),
//				webFluxContentTypeResolver, webFluxAdapterRegistry) {
//			@Override
//			public Mono<Void> handleResult(ServerWebExchange exchange, HandlerResult result) {
//
//				PathContainer lookupPath = exchange.getRequest().getPath().pathWithinApplication();
//				if (ignore(lookupPath, exchange)) {
//					Object body = result.getReturnValue();
//					MethodParameter bodyTypeParameter = result.getReturnTypeSource();
//					return writeBody(body, bodyTypeParameter, exchange);
//				} else {
//					Object data = result.getReturnValue();
//					ArgumentBox argumentBox = exchange.getAttribute(ActionConstant.ARGUMENT_BOX);
//					String body = exchange.getAttribute(ActionConstant.REQUEST_BODY_CACHE);
//					data = rh.result(data, body, argumentBox);
//					MethodParameter bodyTypeParameter = result.getReturnTypeSource();
//					return writeBody(data, bodyTypeParameter, exchange);
//				}
//			}
//		};
//	}

	private boolean ignore(PathContainer lookupPath, ServerWebExchange exchange) {
		List<PathPattern> matches = excludeList.stream()
				.filter(key -> key.matches(lookupPath))
				.collect(Collectors.toList());
		if (matches.isEmpty()) {
			return false;
		}
		return true;
	}
}
